import jmcvideo.*;

int mWidth = 640;
int mHeight = 480;
FaceMovie currMovie;
PImage faceMask;

PImage a, b, c, d, e, f, g, h, i; // a-i represent the image grid that we'll chop video into  
float speed=.7; // speed is our movement % for smoothing the warp centerpoint as it chases the mouse (set to 1 for instant tracking)
int fbL = 100; // face bounds, distance from (Left, Top, Right, Bottom) 
int fbT = 150; 
int fbR = 320;
int fbB = 150;
float Xexpansion = 1.3;
float Yexpansion = 2.3;
int fbLe = int(fbL*Xexpansion); // face bounds, after pushed in
int fbTe = int(fbT*Yexpansion); 
int fbRe = int(fbR*Xexpansion);
int fbBe = int(fbB*Yexpansion);

int drawState;
String movieDir = "/Users/enguyen/Documents/Projects/split personality reorder/splitpersonalityreoder/faceplayer/data/movies";
HashMap fileTree = new HashMap(); // maps each file number to an array of children
HashMap fileNames = new HashMap(); // keyed by clip number
HashMap faceScales = new HashMap(); // keyed by clip number
ArrayList faceloopClips = new ArrayList();
boolean movieLoading = false; // for "thread safety"
final float effectAmount = 0.75;

void setup() {
  size(mWidth, mHeight, P2D);
  background(0);
  buildFileTree();
  
  // initialize
  currMovie = new FaceMovie(this, "movies/" + fileNames.get(1));
  
  // mask
  faceMask = loadImage("facemask.jpg");
  faceMask.resize(mWidth, mHeight);
  
  // start!
  currMovie.load(15);
}

void draw() {
  // if we're done playing this movie, move on to the next.
  if (currMovie.isDone()) {
    buildFileTree();
    int nextMovieNum = findNextMovieNumber(currMovie.getNum());
    currMovie.load( nextMovieNum );   
    
  // if we're still playing the movie
  } else {
    if (currMovie.isFrozen()) { // restart in case of freeze
      float currMovieTime = currMovie.time();
      println("Clip Froze at " + currMovieTime + ". Restarting.");
      currMovie.stop();
      currMovie.setCurrentTime(currMovieTime); // doesn't seem to be working
      currMovie.play();
    } else {
      background(0);
      currMovie.mask(faceMask);
      
      // Do the math for the scaling factor. 
      // Scaling occurs from the center line of the face (y) and at the eyes (x)
      // We do this by calculating a smaller box using temp face box bounds on input
      // and larger temp extended boxes on output.
      float faceScale = currMovie.getFaceScale();
      int tempfbL = int((fbL*faceScale)+(fbR*(faceScale-1))); // mouth line takes in eye line shrinkage
      int tempfbT = int(fbT*faceScale);
      int tempfbR = fbR; // eye line remains static
      int tempfbB = int(fbB*faceScale);
      int tempfbLe = int((fbLe*faceScale)+(fbRe*(faceScale-1))); // mouth line takes in eye line shrinkage
      int tempfbTe = int(fbTe*faceScale);
      int tempfbRe = fbRe; // eye line remains static
      int tempfbBe = int(fbBe*faceScale);
      
      // Divide up into a 3x3 grid, using face box dimensions.
      a = currMovie.get(0, 0, tempfbL, tempfbT);  
      b = currMovie.get(tempfbL, 0, width-tempfbR-tempfbL, tempfbT);
      c = currMovie.get(width-tempfbR, 0, tempfbR, tempfbT);  
      d = currMovie.get(0, tempfbT, tempfbL, height-tempfbB-tempfbT);  
      e = currMovie.get(tempfbL, tempfbT, width-tempfbR-tempfbL, height-tempfbB-tempfbT);
      f = currMovie.get(width-tempfbR, tempfbT, tempfbR, height-tempfbB-tempfbT);  
      g = currMovie.get(0, height-tempfbB, tempfbL, tempfbR);  
      h = currMovie.get(tempfbL, height-tempfbB, width-tempfbR-tempfbL, tempfbR);
      i = currMovie.get(width-tempfbR, height-tempfbB, tempfbR, tempfbR);  
      
      image(a, fbL-tempfbLe, fbT-tempfbTe, tempfbLe, tempfbTe);  
      image(b, fbL, fbT-tempfbTe, width-fbR-fbL, tempfbTe);
      image(c, width-fbR, fbT-tempfbTe, tempfbRe, tempfbTe);  
      image(d, fbL-tempfbLe, fbT, tempfbLe, height-fbB-fbT);  
      image(e, fbL, fbT, width-fbR-fbL, height-fbB-fbT);
      image(f, width-fbR, fbT, tempfbRe, height-fbB-fbT);  
      image(g, fbL-tempfbLe, height-fbB, tempfbLe, tempfbBe);  
      image(h, fbL, height-fbB, width-fbR-fbL, tempfbBe);
      image(i, width-fbR, height-fbB, tempfbRe, tempfbBe);
      
      // Debugging frame: this is where the face is left undistorted.
      //stroke(255,255,255);
      //noFill();
      //rect(fbL, fbT, width-fbL-fbR, height-fbT-fbB);
    }
  }
}

int findNextMovieNumber(int afterNum) {
  //buildFileTree();
  println("Last Played: Clip " + afterNum);
  // pick the next movie
  int nextMovieNum;
  if (afterNum < 0) { 
    nextMovieNum = 1; // after a loop, return to the root
  } else if (fileTree.containsKey(afterNum)) {
    int[] children = (int[]) fileTree.get(afterNum);
    nextMovieNum = children[int(random(children.length))];
  } else {
    nextMovieNum = -1;
  }
  println("Now Playing: Clip " + nextMovieNum);
  return nextMovieNum;
}

void buildFileTree() {
  File file = new File( movieDir );
  String names[] = file.list();
  println("--- Building File Tree ---");
  for (int i = 0; i < names.length; i++) {
    String name = names[i];
    if (name.indexOf("face") != 0) continue; // skip non-"face" files
    int faceloopDelimiter = name.indexOf("loop");
    if (faceloopDelimiter > -1) {
      if (!faceloopClips.contains(name)) faceloopClips.add(name);
      continue; 
    }
    // from the file name, determine its number and which it was in response to
    int replyDelimiter = name.indexOf("r");
    int scaleDelimiter = name.indexOf("s");
    int endFileName = name.indexOf(".");
    int fileNumber = int(name.substring(4,replyDelimiter));
    int replyToFileNumber = 
      int(name.substring(replyDelimiter+1, scaleDelimiter > -1 ? scaleDelimiter : endFileName));
    println("File " + name + " number " + fileNumber + " in response to " + replyToFileNumber);
    
    if (scaleDelimiter > -1) {
      faceScales.put(fileNumber, int(name.substring(scaleDelimiter+1, endFileName)));
    }
    
    // add the entry to the fileTree
    if (!fileTree.containsKey(replyToFileNumber)) {
      int[] children = {fileNumber};
      fileTree.put(replyToFileNumber, children);
    } else {
      int[] children = (int[]) fileTree.get(replyToFileNumber);
      boolean isNewChild = true;
      for (int j = 0; j < children.length; j++) {
        if (fileNumber == children[j]) isNewChild = false;
      }
      if (isNewChild) children = append(children, fileNumber);
      fileTree.put(replyToFileNumber, children);
    }
    
    // add the entry to the fileNames hash, keyed by file number
    fileNames.put(fileNumber, name);
  }
  printTree();
  println("--------------------------");
}

void printTree() {
  Set keys = fileTree.keySet();
  Iterator k = keys.iterator();
  while(k.hasNext()) {
    Object key = k.next();
    int[] values = (int[]) fileTree.get(key);
    String valuesString = "";
    for (int i = 0; i < values.length; i++) {
      valuesString += values[i] + ", ";
    }
    println(key + " => " + valuesString);
  }
}

